package cn.yll.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * redis操作工具
 * @author yangll
 */
@Slf4j
@Component
public class RedisUtil {

    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    RedissonClient redissonClient;

    /**各环境隔离，key设置通用前缀*/
    @Value("${spring.application.name}-${spring.profiles.active}-")
    private String keyPrefix;

    public String getKeyPrefix() {
        return keyPrefix;
    }

    public String buildKey(String key) {
        return keyPrefix + key;
    }

    /**
     * 删除一个key
     * @return false表示key不存在 true表示删除成功
     */
    public Boolean delete(String key){
        return stringRedisTemplate.delete(buildKey(key));
    }

    /**
     * 根据key获取值
     */
    public String getV(String key){
        return stringRedisTemplate.opsForValue().get(buildKey(key));
    }

    /**
     * 把一堆key value设置到redis中，并同时设置过期时间
     */
    public void set(String key, String value, long time, TimeUnit timeUnit) {
        stringRedisTemplate.opsForValue().set(buildKey(key), value, time, timeUnit);
    }

    /**
     * 判断一个key是不是在redis中存在
     * @return true存在 false不存在
     */
    public Boolean hasKey(String key) {
        return stringRedisTemplate.hasKey(buildKey(key));
    }

    /**
     * 给一个key重新设置过期时间
     * @return false表示key不存在
     */
    public Boolean setTime(String key, long time) {
        return setTime(key, time, TimeUnit.SECONDS);
    }

    /**
     * 给一个key设置过期时间
     * @return false表示key不存在
     */
    public Boolean setTime(String key, long time, TimeUnit timeUnit) {
        return stringRedisTemplate.boundValueOps(buildKey(key)).expire(time, timeUnit);
    }

    /**
     * 设置一个计数器，并使之+1，同时设置有效期，单位秒
     * @return 返回值+1后的结果
     */
    public Long increment(String key, long seconds) {
        Long num = stringRedisTemplate.opsForValue().increment(buildKey(key), 1L);
        if (Objects.equals(num, 1L)) {
            setTime(key, seconds, TimeUnit.SECONDS);
        }
        return num;
    }

    /**
     * 设置一个计数器并使之-1
     * @return 返回值-1后的结果
     */
    public Long decrement(String key) {
        return stringRedisTemplate.opsForValue().increment(buildKey(key), -1L);
    }

    /**
     * 根据key获取数值
     */
    public Long getLongV(String key){
        String num = stringRedisTemplate.opsForValue().get(buildKey(key));
        if (StringUtils.hasText(num)) {
            return Long.valueOf(num);
        }
        return 0L;
    }

    /**
     * 如果key不存在，则存入这个key-value对
     * @return true 设置成功 false未设置
     */
    public Boolean setNX(String key, String value) {
        return stringRedisTemplate.opsForValue().setIfAbsent(buildKey(key), value);
    }

    /**
     * 如果key不存在，则存入这个key-value对,同时设置缓存时间
     * @param seconds 自动过期时长
     * @return true 设置成功 false未设置
     */
    public Boolean setNX(String key, String value, int seconds) {
        return stringRedisTemplate.opsForValue().setIfAbsent(buildKey(key), value, Duration.ofSeconds(seconds));
    }

    /**
     * 创建一个名字为key的锁
     * lock.lock(); // 等待获取到锁后执行业务
     * lock.tryLock(); // 尝试获得锁 ，false表示未取到 true表示获得锁
     * lock.tryLock(2, 10, TimeUnit.Seconds); //2秒内尝试获得锁，10秒后这把锁释放，防止死锁 （推荐使用这个方式上锁）
     */
    public RLock getLock(String key) {
        return redissonClient.getLock(buildKey(key));
    }

    /**
     * 创建一个每rate秒生产num个令牌的限流器
     * @param key 限流器名
     * @param rate 每单位时间
     * @param num 单位时间的令牌数
     * @param unit 时间单位
     */
    public RRateLimiter initRateLimiter(String key, long rate, long num, RateIntervalUnit unit) {
        RRateLimiter limiter = redissonClient.getRateLimiter(buildKey(key));
        limiter.trySetRate(RateType.PER_CLIENT, rate, num, RateIntervalUnit.SECONDS);
        return limiter;
    }

    /**
     * 获取限流器
     */
    public RRateLimiter getRateLimiter(String key) {
        return redissonClient.getRateLimiter(buildKey(key));
    }

    /**
     * 获取String类型List集合
     */
    public RList<String> getRList(String key) {
        return redissonClient.getList(buildKey(key));
    }

    /**
     * 获取一个String类型普通队列
     */
    public RQueue<String> getRQueue(String key) {
        return redissonClient.getDeque(buildKey(key));
    }

    /**
     * 获取一个String类型线程阻塞队列
     * queue.take()队列为空时阻塞线程执行
     */
    public RBlockingQueue<String> getRBlockingQueue(String key) {
        return redissonClient.getBlockingQueue(buildKey(key));
    }

    /**
     * 获取一个String类型的延迟队列
     *
     * @param key 队列名称
     */
    public RDelayedQueue<String> getRDelayedQueue(String key) {
        RQueue<String> queue = redissonClient.getQueue(buildKey(key));
        return redissonClient.getDelayedQueue(queue);
    }
}
